﻿using System;
using System.Collections.Generic;
using System.Numerics;
using System.Drawing;
using System.Reflection;
using ArgusLib.Xml;
using ArgusLib.Drawing;
using ArgusLib.Threading;
using SMath = System.Math;

namespace ArgusLib.Fractals.OrbitFractals
{
	public class RendererRouteLength : Renderer<IDataSet, RenderParameterRouteLength>
	{
		delegate Color ColorProvider(double routeLength, double frequency, double translation);

		#region Constructors
		public RendererRouteLength(RenderParameterRouteLength parameter)
			: base(parameter) { }
		#endregion

		#region Public Methods
		public override IDataSetProvider GetCompatibleDataSetProvider()
		{
			return new DataSetRouteLengthProvider();
		}

		public override bool SupportsDataSet(IDataSet dataSet)
		{
			return (dataSet is IDataSetRouteLengths && dataSet is IDataSetIterations);
		}

		public override bool SupportsDataSet(Type dataSetType)
		{
			return (dataSetType.Is(typeof(IDataSetRouteLengths)) == true && dataSetType.Is(typeof(IDataSetIterations)) == true);
		}

		protected override void RenderCore(ExtendedBitmap image, IDataSet dataSet)
		{
			IDataSetRouteLengths dataSetRL = (IDataSetRouteLengths)dataSet;
			IDataSetIterations dataSetI = (IDataSetIterations)dataSet;
			image.Fill(this.Parameter.BackgroundColor);

			double freq = this.GetFrequency(dataSetRL);
			double trans = this.GetTranslation();
			ColorProvider colorProvider = this.GetColorProvider();

			for (int x = 0; x < image.Width; x++)
			{
				for (int y = 0; y < image.Height; y++)
				{
					if (dataSet.IsNull[x, y] == true || dataSetI.Iterations[x, y] >= dataSetI.CalculationParameter.Iterations)
					{
						image.BlendPixel(x, y, this.Parameter.SetColor);
					}
					else
					{
						Color c = colorProvider(dataSetRL.RouteLengths[x, y], freq, trans);
						image.BlendPixel(x, y, c);
					}
				}
			}
		}
		#endregion

		#region Non-Public Methods
		private double GetFrequency(IDataSetRouteLengths dataSet)
		{
			if (this.Parameter.AutoSetFrequency == false)
				return this.Parameter.Frequency;

			if (this.Parameter.Coloring == RenderParameterRouteLength.Colorings.HSV)
				return this.Parameter.NumberOfPeriods * 360 / dataSet.IntervalRouteLengths.Range;
			else
				return this.Parameter.NumberOfPeriods * 2 * SMath.PI / dataSet.IntervalRouteLengths.Range;
		}

		private double GetTranslation()
		{
			if (this.Parameter.Coloring == RenderParameterRouteLength.Colorings.HSV)
				return this.Parameter.Translation * 360;
			else
				return this.Parameter.Translation * 2 * SMath.PI;
		}

		private ColorProvider GetColorProvider()
		{
			Type type = typeof(RendererRouteLength);
			MethodInfo mi = type.GetMethod("ColorProvider_" + this.Parameter.Coloring.ToString(), BindingFlags.NonPublic | BindingFlags.Static| BindingFlags.Instance);
			if (mi == null)
				return null;
			else if (mi.IsStatic == true)
				return Delegate.CreateDelegate(typeof(ColorProvider), mi) as ColorProvider;
			else
				return Delegate.CreateDelegate(typeof(ColorProvider), this, mi) as ColorProvider;

		}
		#endregion

		#region ColorProvider Methods
		private static Color ColorProvider_HSV(double routeLength, double frequency, double translation)
		{
			return ColorExtension.FromHSV(frequency * routeLength + translation, 1, 1);
		}

		private static Color ColorProvider_AllColors(double routeLength, double frequency, double translation)
		{
			byte[] b = GetColorBytes_3(routeLength, frequency, translation);
			return Color.FromArgb(255, b[0], b[1], b[2]);
		}

		private static Color ColorProvider_RedYellow(double routeLength, double frequency, double translation)
		{
			return GetColorUserDefined2(routeLength, frequency, translation, Color.Red, Color.Yellow);
		}

		private static Color ColorProvider_BluePurple(double routeLength, double frequency, double translation)
		{
			return GetColorUserDefined2(routeLength, frequency, translation, Color.Blue, Color.White);
		}

		private Color ColorProvider_UserDefined1(double routeLength, double frequency, double translation)
		{
			return GetColorUserDefined1(routeLength, frequency, translation, this.Parameter.UserColor1, this.Parameter.UserColor2);
		}

		private Color ColorProvider_UserDefined2(double routeLength, double frequency, double translation)
		{
			return GetColorUserDefined2(routeLength, frequency, translation, this.Parameter.UserColor1, this.Parameter.UserColor2);
		}

		private static Color GetColorUserDefined2(double routeLength, double frequency, double translation, Color color1, Color color2)
		{
			int dr = (color2.R - color1.R) / 2;
			int dg = (color2.G - color1.G) / 2;
			int db = (color2.B - color1.B) / 2;
			double arg = frequency * routeLength + translation;
			return Color.FromArgb(
				255,
				(byte)((SMath.Sin(arg)+1) * dr + color1.R),
				(byte)((SMath.Sin(arg + 2 * SMath.PI / 3) + 1) * dg + color1.G),
				(byte)((SMath.Sin(arg + 4 * SMath.PI / 3) + 1) * db + color1.B));
		}

		private static Color GetColorUserDefined1(double routeLength, double frequency, double translation, Color color1, Color color2)
		{
			double x = (SMath.Sin(frequency * routeLength + translation) + 1) / 2;
			if (color2.A == 0)
			{
				return Color.FromArgb((byte)(color1.A * (1 - x)), color1);
			}

			byte a = (byte)(color2.A * x);
			color2 = Color.FromArgb(a, color2);
			return ColorExtension.Blend(color2, color1, BlendMode.Alpha);

			//int dr = (color2.R - color1.R) / 2;
			//int dg = (color2.G - color1.G) / 2;
			//int db = (color2.B - color1.B) / 2;
			//double arg = frequency * routeLength + translation;
			//return Color.FromArgb(
			//	255,
			//	(byte)((SMath.Sin(arg) + 1) * dr + color1.R),
			//	(byte)((SMath.Sin(arg) + 1) * dg + color1.G),
			//	(byte)((SMath.Sin(arg) + 1) * db + color1.B));
		}

		private static byte[] GetColorBytes_2(double routeLength, double frequency, double translation)
		{
			return new byte[]{
				(byte)(127.5+127.5*SMath.Sin(frequency*routeLength+translation)),
				(byte)(127.5+127.5*SMath.Sin(frequency*routeLength+translation+SMath.PI))};
		}

		private static byte[] GetColorBytes_3(double routeLength, double frequency, double translation)
		{
			return new byte[]{
				(byte)(127.5+127.5*SMath.Sin(frequency*routeLength+translation)),
				(byte)(127.5+127.5*SMath.Sin(frequency*routeLength+translation+2*SMath.PI/3)),
				(byte)(127.5+127.5*SMath.Sin(frequency*routeLength+translation+4*SMath.PI/3))};
		}
		#endregion
	}
}
